using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using SkinnedModel;



namespace CropCircles
{

    /// <summary>
    /// This is borrowed directly from the www.riemers.net tutorial series 4 on Advanced terrain
    /// </summary>
    public struct VertexMultitextured
    {
        public Vector3 Position;
        public Vector3 Normal;
        public Vector4 TextureCoordinate;
        public Vector4 TexWeights;

        public static int SizeInBytes = (3 + 3 + 4 + 4) * sizeof(float);
        public static VertexElement[] VertexElements = new VertexElement[]
        {
            new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0 ),
            new VertexElement(sizeof(float) * 3, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0),
            new VertexElement(sizeof(float) * 6, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 0),
            new VertexElement(sizeof(float) * 10, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 1),
        };

    }


    /// <summary>
    /// This is a copied struct from the Riemer's Tutorial Series 4 on advanced Terrain Generation
    /// </summary>
    public struct VertexPositionNormalTexture
    {
        public Vector3 Position;
        public Color Color;
        public Vector3 Normal;

        public static int SizeInBytes = 7 * 4;
        public readonly static VertexDeclaration VertexDeclaration = new VertexDeclaration
            (
        
            new VertexElement( 0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0 ),
            new VertexElement(sizeof(float) * 3, VertexElementFormat.Color, VertexElementUsage.Color, 0),
            new VertexElement(sizeof(float) * 3 + 4, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0)

            );
    }

    /// <summary>
    /// This is the main type for your game
    /// 
    /// It has been modified to include code from the Riemer's Tutorial Series 4 for Advanced landscapes
    /// </summary>
    public class CropCircles : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        GraphicsDevice device;

        Vector3 lastMovedVector;

        //Collision Enumeration
        enum CollisionType { None, Building, Boundary, Target }

        //collision bounding boxes
        BoundingBox[] cropCircleBoundingBoxes;  //represents groups of corn crops that the alien will not want to cross.
        BoundingBox completeLandscapeBox;  // Bounding box representing the movable area for the entire landscape.

        int terrainWidth;
        int terrainLength;
        float[,] heightData;

        VertexBuffer terrainVertexBuffer;
        IndexBuffer terrainIndexBuffer;
        VertexDeclaration terrainVertexDeclaration;
        //VertexPositionNormalColored[] vertices;
        VertexMultitextured[] terrainVertices;
        int[] indices;

        Effect effect;
        Matrix viewMatrix;
        Matrix projectionMatrix;
        Matrix reflectionViewMatrix;

        Vector3 cameraStartPosition = new Vector3(0, .1f, 15);
        Vector3 cameraPos;
        float leftRightRot = 0;
        float updownRot = 0;
        const float rotationSpeed = 0.02f;
        const float moveSpeed = 30.0f;

        AnimationPlayer playerStand;
        AnimationPlayer playerWalk;
        AnimationClip clipStand;
        AnimationClip clipWalk;
        SkinningData skinWalk;
        SkinningData skinStand;
        Model walkingModel;
        Model standingModel;
        Model currentModel;
        Model landscapeModel;

        KeyboardState currentKeyboardState = new KeyboardState();
        GamePadState currentGamePadState = new GamePadState();
        KeyboardState previousKeyboardState = new KeyboardState();
        MouseState originalMouseState;

        Texture2D grassTexture;
        Texture2D riverBedTexture;
        Texture2D rockyTexture;
        Texture2D snowTexture;
        Texture2D cloudMap;
        Texture2D waterBumpMap;
        

        Vector3 windDirection = new Vector3(1, 0, 0);

        Model skyBox;
        
        private static float skyscale = 100f;

        // The following is used to help setup the water

        const float waterHeight = 7.5f;
        RenderTarget2D refractionRenderTarget;
        Texture2D refractionMap;

        RenderTarget2D reflectionRenderTarget;
        Texture2D reflectionMap;

        VertexBuffer waterVertexBuffer;
        VertexDeclaration waterVertexDeclaration;

        // The aspect ratio determines how to scale 3d to 2d projection.
        float aspectRatio;
      

        public CropCircles()
        {
            graphics = new GraphicsDeviceManager(this);
            

            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            graphics.PreferredBackBufferWidth = 1024;
            graphics.PreferredBackBufferHeight = 768;

            graphics.ApplyChanges();
            Window.Title = "Crop Circles V1.0";
            

            base.Initialize();
        }


        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        /// 
        // Set the 3d Model to draw.
        

        protected override void LoadContent()
        {
            device = GraphicsDevice;

            effect = Content.Load<Effect>("Effects\\Series4Effects");
            UpdateViewMatrix();
            projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, device.Viewport.AspectRatio, 0.3f, 10000.0f);

            

            Mouse.SetPosition(device.Viewport.Width / 2, device.Viewport.Height / 2);
            originalMouseState = Mouse.GetState();

            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            standingModel = Content.Load<Model>("Characters\\Alien Standing_Around");
            walkingModel = Content.Load<Model>("Characters\\Alien Walking");
            currentModel = standingModel;
            skinStand = standingModel.Tag as SkinningData;
            skinWalk = walkingModel.Tag as SkinningData;

            playerStand = new AnimationPlayer(skinStand);
            playerWalk = new AnimationPlayer(skinWalk);

            clipStand = skinStand.AnimationClips["Standing_Around"];
            clipWalk = skinWalk.AnimationClips["Walking"];

            playerStand.StartClip(clipStand);
            playerWalk.StartClip(clipWalk);


            // the following loads the skybox information
            skyBox = Content.Load<Model>("SkyBox\\SkyBox");
            skyBox.Meshes[0].MeshParts[0].Effect = effect.Clone();
           // skyEffect = (BasicEffect)skyBox.Meshes[0].Effects[0];
            aspectRatio = graphics.GraphicsDevice.Viewport.AspectRatio;

            //the following sets up the refraction map for displaying water.
            PresentationParameters pp = device.PresentationParameters;
            refractionRenderTarget = new RenderTarget2D(device, pp.BackBufferWidth, pp.BackBufferHeight, false, pp.BackBufferFormat, pp.DepthStencilFormat);

            reflectionRenderTarget = new RenderTarget2D(device, pp.BackBufferWidth, pp.BackBufferHeight, false, pp.BackBufferFormat, pp.DepthStencilFormat);

            LoadVertices();
            LoadTextures();
            landscapeModel = Content.Load<Model>("landscape\\landscape");

            SetUpBoundingBoxes();
            
        }

        private CollisionType CheckCollision(BoundingBox box)
        {
            //the following lines will need to be updated to check for the crop sections
            //for (int i = 0; i < buildingBoundingBoxes.Length; i++)
            //    if (buildingBoundingBoxes[i].Contains(box) != ContainmentType.Disjoint)
            //        return CollisionType.Building;

            if (completeLandscapeBox.Contains(box) != ContainmentType.Contains)
                return CollisionType.Boundary;

            return CollisionType.None;
        }
        private void SetUpBoundingBoxes()
        {
            int landscapeWidth = 3500;
            int landscapeLength = 3500;

            //All the following commented out code needs to be rewritten in order to add all the crop sections into a list of bounding boxes or spheres
            //or something in order to check for collisions with the alien.  We are skipping this for now and just creating the whole landscape bounding box
            //List<BoundingBox> bbList = new List<BoundingBox>(); for (int x = 0; x < landscapeWidth; x++)
            //{
            //    for (int z = 0; z < landscapeLength; z++)
            //    {
            //        int buildingType = floorPlan[x, z];
            //        if (buildingType != 0)
            //        {
            //            int buildingHeight = buildingHeights[buildingType];
            //            Vector3[] buildingPoints = new Vector3[2];
            //            buildingPoints[0] = new Vector3(x, 0, -z);
            //            buildingPoints[1] = new Vector3(x + 1, buildingHeight, -z - 1);
            //            BoundingBox buildingBox = BoundingBox.CreateFromPoints(buildingPoints);
            //            bbList.Add(buildingBox);
            //        }
            //    }
            //}
            //buildingBoundingBoxes = bbList.ToArray();
            Vector3[] boundaryPoints = new Vector3[2];
            boundaryPoints[0] = new Vector3(350, 0, 0);
            boundaryPoints[1] = new Vector3(landscapeWidth, 500, -landscapeLength);
            completeLandscapeBox = BoundingBox.CreateFromPoints(boundaryPoints);
        }

        private Plane CreatePlane(float height, Vector3 planeNormalDirection, Matrix currentViewMatrix, bool clipSide)
        {
            planeNormalDirection.Normalize();
            Vector4 planeCoeffs = new Vector4(planeNormalDirection, height);
            if (clipSide)
            {
                planeCoeffs *= -1;
            }

            //Matrix worldViewProjection = currentViewMatrix * projectionMatrix;
            //Matrix inverseWorldViewProjection = Matrix.Invert(worldViewProjection);
            //inverseWorldViewProjection = Matrix.Transpose(inverseWorldViewProjection);

            //planeCoeffs = Vector4.Transform(planeCoeffs, inverseWorldViewProjection);
            Plane finalPlane = new Plane(planeCoeffs);

            return finalPlane;
        }

        private void LoadTextures()
        {

            grassTexture = Content.Load<Texture2D>("Textures\\Grass");
            riverBedTexture = Content.Load<Texture2D>("Textures\\Riverbed");
            rockyTexture = Content.Load<Texture2D>("Textures\\Rocky");
            snowTexture = Content.Load<Texture2D>("Textures\\snow");

            cloudMap = Content.Load<Texture2D>("Skybox\\SkyBox Texture");
            waterBumpMap = Content.Load<Texture2D>("Textures\\waterbump");
        }

        private void LoadVertices()
        {
            Texture2D heightMap = Content.Load<Texture2D>("Textures\\Heightmap256");
            LoadHeightData(heightMap);

            
            
            SetUpTerrainVertices();
            SetUpTerrainIndices();
            CalculateNormals();
            CopyToTerrainBuffers();
            SetUpWaterVertices();
            waterVertexDeclaration = new VertexDeclaration(VertexPositionTexture.VertexDeclaration.GetVertexElements());
        }

        private void CopyToTerrainBuffers()
        {
            terrainVertexDeclaration = new VertexDeclaration(VertexMultitextured.VertexElements);
            terrainVertexBuffer = new VertexBuffer(device, terrainVertexDeclaration, terrainVertices.Length, BufferUsage.WriteOnly);
            terrainVertexBuffer.SetData(terrainVertices);

            terrainIndexBuffer = new IndexBuffer(device, typeof(int), indices.Length, BufferUsage.WriteOnly);
            terrainIndexBuffer.SetData(indices);
        }

        private void CalculateNormals()
        {
            for (int i = 0; i < terrainVertices.Length; i++)
            {
                terrainVertices[i].Normal = new Vector3(0, 0, 0);
            }

            for (int i = 0; i < indices.Length / 3; i++)
            {
                int index1 = indices[i * 3];
                int index2 = indices[i * 3 + 1];
                int index3 = indices[i * 3 + 2];

                Vector3 side1 = terrainVertices[index1].Position - terrainVertices[index3].Position;
                Vector3 side2 = terrainVertices[index1].Position - terrainVertices[index2].Position;
                Vector3 normal = Vector3.Cross(side1, side2);

                terrainVertices[index1].Normal += normal;
                terrainVertices[index2].Normal += normal;
                terrainVertices[index3].Normal += normal;
            }

            for (int i = 0; i < terrainVertices.Length; i++)
            {
                terrainVertices[i].Normal.Normalize();
            }
        }

        private void SetUpWaterVertices()
        {
            VertexPositionTexture[] waterVertices = new VertexPositionTexture[6];

            waterVertices[0] = new VertexPositionTexture(new Vector3(0, waterHeight, 0), new Vector2(0, 1));
            waterVertices[2] = new VertexPositionTexture(new Vector3(terrainWidth, waterHeight, -terrainLength), new Vector2(1, 0));
            waterVertices[1] = new VertexPositionTexture(new Vector3(0, waterHeight, -terrainLength), new Vector2(0, 0));

            waterVertices[3] = new VertexPositionTexture(new Vector3(0, waterHeight, 0), new Vector2(0, 1));
            waterVertices[5] = new VertexPositionTexture(new Vector3(terrainWidth, waterHeight, 0), new Vector2(1, 1));
            waterVertices[4] = new VertexPositionTexture(new Vector3(terrainWidth, waterHeight, -terrainLength), new Vector2(1, 0));

            //VertexDeclaration vertexDeclaration = new VertexDeclaration(VertexMultitextured.VertexElements);
            waterVertexBuffer = new VertexBuffer(device, VertexPositionTexture.VertexDeclaration, waterVertices.Count(), BufferUsage.WriteOnly);

            waterVertexBuffer.SetData(waterVertices);
        }

        private void SetUpTerrainIndices()
        {
            indices = new int[(terrainWidth - 1) * (terrainLength - 1) * 6];
            int counter = 0;
            for (int y = 0; y < terrainLength - 1; y++)
            {
                for (int x = 0; x < terrainWidth - 1; x++)
                {
                    int lowerLeft = x + y * terrainWidth;
                    int lowerRight = (x + 1) + y * terrainWidth;
                    int topLeft = x + (y + 1) * terrainWidth;
                    int topRight = (x + 1) + (y + 1) * terrainWidth;

                    indices[counter++] = topLeft;
                    indices[counter++] = lowerRight;
                    indices[counter++] = lowerLeft;

                    indices[counter++] = topLeft;
                    indices[counter++] = topRight;
                    indices[counter++] = lowerRight;
                }
            }
        }

        private void SetUpTerrainVertices()
        {
            terrainVertices = new VertexMultitextured[terrainWidth * terrainLength];

            for (int x = 0; x < terrainWidth; x++)
            {
         for (int y = 0; y < terrainLength; y++)
         {
             terrainVertices[x + y * terrainWidth].Position = new Vector3(x, heightData[x, y], -y);
             terrainVertices[x + y * terrainWidth].TextureCoordinate.X = (float)x / 30.0f;
             terrainVertices[x + y * terrainWidth].TextureCoordinate.Y = (float)y / 30.0f;

             terrainVertices[x + y * terrainWidth].TexWeights.X = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 0) / 8.0f, 0, 1);
             terrainVertices[x + y * terrainWidth].TexWeights.Y = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 12) / 6.0f, 0, 1);
             terrainVertices[x + y * terrainWidth].TexWeights.Z = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 20) / 6.0f, 0, 1);
             terrainVertices[x + y * terrainWidth].TexWeights.W = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 30) / 6.0f, 0, 1);

             float total = terrainVertices[x + y * terrainWidth].TexWeights.X;
             total += terrainVertices[x + y * terrainWidth].TexWeights.Y;
             total += terrainVertices[x + y * terrainWidth].TexWeights.Z;
             total += terrainVertices[x + y * terrainWidth].TexWeights.W;

             terrainVertices[x + y * terrainWidth].TexWeights.X /= total;
             terrainVertices[x + y * terrainWidth].TexWeights.Y /= total;
             terrainVertices[x + y * terrainWidth].TexWeights.Z /= total;
             terrainVertices[x + y * terrainWidth].TexWeights.W /= total;
         }
            }
 
    
        }
        

        private void LoadHeightData(Texture2D heightMap)
        {
            float minimumHeight = float.MaxValue;
            float maximumHeight = float.MinValue;

            terrainWidth = heightMap.Width;
            terrainLength = heightMap.Height;

            Color[] heightMapColors = new Color[terrainWidth * terrainLength];
            heightMap.GetData(heightMapColors);

            heightData = new float[terrainWidth, terrainLength];
            for (int x = 0; x < terrainWidth; x++)
            {
                for (int y = 0; y < terrainLength; y++)
                {
                    heightData[x, y] = heightMapColors[x + y * terrainWidth].R;
                    if (heightData[x, y] < minimumHeight)
                        minimumHeight = heightData[x, y];
                    if (heightData[x, y] > maximumHeight)
                        maximumHeight = heightData[x, y];
                }
            }

            for (int x = 0; x < terrainWidth; x++)
            {
                for (int y = 0; y < terrainLength; y++)
                {
                    heightData[x, y] = (heightData[x, y] - minimumHeight) / (maximumHeight - minimumHeight) * 30.0f;
                }
            }

            

        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            HandleInput();

            CheckWalkOrStand(gameTime);
            float timeDifference = (float)gameTime.ElapsedGameTime.TotalMilliseconds / 1000.0f;
            CheckMoveOrRotateModel();

            playerWalk.Update(gameTime.ElapsedGameTime, true, Matrix.Identity);
            playerStand.Update(gameTime.ElapsedGameTime, true, Matrix.Identity);
            UpdateViewMatrix();
            // Collision Detection
            BoundingBox alienBoundingBox = new BoundingBox(modelPosition,modelPosition * (new Vector3(2,2,2)));
            if (CheckCollision(alienBoundingBox) != CollisionType.None)
            {
                modelPosition -= lastMovedVector;
                
                
            }

            base.Update(gameTime);
        }

        

        private void UpdateViewMatrix()
        {

            cameraRotation = Quaternion.Lerp(cameraRotation, modelRotation, 0.05f);

            
            cameraPos = Vector3.Transform(cameraStartPosition, Matrix.CreateFromQuaternion(cameraRotation));
            

            cameraPos += modelPosition;
            Vector3 cameraOriginalUpVector = new Vector3(0, 1, 0);
            Vector3 cameraRotatedUpVector = Vector3.Transform(cameraOriginalUpVector, cameraRotation);

            viewMatrix = Matrix.CreateLookAt(cameraPos, modelPosition, cameraRotatedUpVector);
          // projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, device.Viewport.AspectRatio, 0.2f, 500.0f);
            //This code sets up a camera for the ReflectionMap
            Vector3 reflCameraPosition = cameraPos;
            reflCameraPosition.Y = -cameraPos.Y + waterHeight * 2;
            Vector3 reflTargetPos = modelPosition;
            reflTargetPos.Y = -modelPosition.Y + waterHeight * 2;

            Vector3 cameraRight = Vector3.Transform(new Vector3(1, 0, 0), modelRotation);
            Vector3 invUpVector = Vector3.Cross(cameraRight, reflTargetPos - reflCameraPosition);

            reflectionViewMatrix = Matrix.CreateLookAt(reflCameraPosition, reflTargetPos, invUpVector);
        }

        //private void AddToModelPosition(Vector3 vectorToAdd)
        //{
        //    Matrix modelRotation = Matrix.CreateRotationX(updownRot) * Matrix.CreateRotationY(leftRightRot);
        //    Vector3 rotatedVector = Vector3.Transform(vectorToAdd, modelRotation);
        //    modelPosition += moveSpeed * rotatedVector;
        //    UpdateViewMatrix();

            
        //}

        #region Drawing Region


        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        /// 
        // Set the position of the model in world space, and set the rotation.
        Vector3 modelPosition = new Vector3(350,200,-600);
        Quaternion cameraRotation = Quaternion.Identity;
        Vector3 landscapePosition = new Vector3(0, -225,0);
        Vector3 waterPosition = new Vector3(-2048,-200,2048);
        Quaternion modelRotation = Quaternion.Identity;

        // Set the position of the camera in world space, for our view matrix.
      //  Vector3 cameraPosition = new Vector3(0.0f, 0.0f, 10.0f);

        protected override void Draw(GameTime gameTime)
        {
            float time = (float)gameTime.TotalGameTime.TotalMilliseconds / 100.0f;
            RasterizerState rs = new RasterizerState();
            rs.CullMode = CullMode.None;
            device.RasterizerState = rs;
            DrawRefractionMap();
            DrawReflectionMap();
            
            device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);

            DrawSkyDome();
            //DrawTerrain(viewMatrix);
            
            DrawLandScapeModel(viewMatrix);
            DrawWater(time);
            DrawAlien();

          DrawPIPWindow();
            

            base.Draw(gameTime);
        }

       

        private void DrawPIPWindow()
        {
            SpriteBatch spriteBatch = new SpriteBatch(device);
            spriteBatch.Begin();
            Vector2 pos = new Vector2(graphics.PreferredBackBufferWidth - (graphics.PreferredBackBufferWidth / 10), 0);
            spriteBatch.Draw(refractionMap, pos, null, Color.White, 0f, Vector2.Zero, 0.1f, SpriteEffects.None, 0f);
            
            Vector2 pos2 = new Vector2(graphics.PreferredBackBufferWidth - (graphics.PreferredBackBufferWidth/10), graphics.PreferredBackBufferHeight/10);
            spriteBatch.Draw(reflectionMap, pos2, null, Color.White, 0f, Vector2.Zero, 0.1f, SpriteEffects.None, 0f);
            spriteBatch.End();
        }

        private void DrawLandScapeModel(Matrix viewM)
        {
            // Copy any parent transforms.
            Matrix[] transforms = new Matrix[landscapeModel.Bones.Count];
            landscapeModel.CopyAbsoluteBoneTransformsTo(transforms);

            Matrix wMatrix = Matrix.CreateTranslation(0, -0.4f, 0) * Matrix.CreateScale(200) * Matrix.CreateTranslation(landscapePosition);
            // Draw the model. A model can have multiple meshes, so loop.
            foreach (ModelMesh mesh in landscapeModel.Meshes)
            {
                // This is where the mesh orientation is set, as well 
                // as our camera and projection.
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    Matrix worldMatrix = transforms[mesh.ParentBone.Index] * wMatrix;
                    
                    effect.World = worldMatrix;
                    effect.View = viewM;
                    effect.Projection = projectionMatrix;
                }
                // Draw the mesh, using the effects set above.
                mesh.Draw();
            }

            
        }

        private void DrawAlien()
        {
            Matrix[] bones;
            if (currentModel.Equals(walkingModel))
            {
                bones = playerWalk.GetSkinTransforms();
            }
            else
            {
                bones = playerStand.GetSkinTransforms();
            }
            
            


            //Render the skinned mesh


            DrawMesh(bones, ref viewMatrix, ref projectionMatrix, currentModel);
        }

        


        //private void DrawTerrain(Matrix currentViewMatrix)
        //{
        //    effect.CurrentTechnique = effect.Techniques["MultiTextured"];
        //    effect.Parameters["xTexture0"].SetValue(riverBedTexture);
        //    effect.Parameters["xTexture1"].SetValue(grassTexture);
        //    effect.Parameters["xTexture2"].SetValue(rockyTexture);
        //    effect.Parameters["xTexture3"].SetValue(snowTexture);

        //    Matrix worldMatrix = Matrix.Identity;
        //    effect.Parameters["xWorld"].SetValue(worldMatrix);
        //    effect.Parameters["xView"].SetValue(currentViewMatrix);
        //    effect.Parameters["xProjection"].SetValue(projectionMatrix);

        //    effect.Parameters["xEnableLighting"].SetValue(true);
        //    effect.Parameters["xAmbient"].SetValue(0.4f);
        //    effect.Parameters["xLightDirection"].SetValue(new Vector3(-0.5f, -1, -0.5f));
        //    foreach (EffectPass pass in effect.CurrentTechnique.Passes)
        //    {
        //        pass.Apply();

        //        device.Indices = terrainIndexBuffer;
        //        device.SetVertexBuffer(terrainVertexBuffer);

        //        device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, terrainVertices.Length, 0, indices.Length / 3);
        //    }

         

            
        //}

        private void DrawSkyDome()
        {
            

            device.DepthStencilState = DepthStencilState.None;
            Matrix[] modelTransforms = new Matrix[skyBox.Bones.Count];
            skyBox.CopyAbsoluteBoneTransformsTo(modelTransforms);
            Matrix wMatrix = Matrix.CreateTranslation(0, -0.4f, 0) * Matrix.CreateScale(skyscale) * Matrix.CreateTranslation(cameraPos);

            foreach (ModelMesh mesh in skyBox.Meshes)
            {
                foreach (Effect currentEffect in mesh.Effects)
                {
                    Matrix worldMatrix = modelTransforms[mesh.ParentBone.Index] * wMatrix;
                    currentEffect.CurrentTechnique = currentEffect.Techniques["Textured"];
                    currentEffect.Parameters["xWorld"].SetValue(worldMatrix);
                    currentEffect.Parameters["xView"].SetValue(viewMatrix);
                    currentEffect.Parameters["xProjection"].SetValue(projectionMatrix);
                    currentEffect.Parameters["xTexture"].SetValue(cloudMap);
                    currentEffect.Parameters["xEnableLighting"].SetValue(false);
                }
                mesh.Draw();
            }
            device.BlendState = BlendState.Opaque;
            device.DepthStencilState = DepthStencilState.Default;
        }
        

        private void DrawMesh(Matrix[] bones, ref Matrix view, ref Matrix projection, Model currentModel)
        {

            Matrix wMatrix = Matrix.CreateScale(0.5f) * Matrix.CreateRotationY(MathHelper.Pi) * Matrix.CreateFromQuaternion(modelRotation) * Matrix.CreateTranslation(modelPosition);
            foreach (ModelMesh mesh in currentModel.Meshes)
            {
                //This is where the mesh orientation is set, as well as our camera
                // and projection.

                
                foreach (SkinnedEffect effect in mesh.Effects)
                {
                    Matrix worldMatrix = bones[mesh.ParentBone.Index] * wMatrix;
                    effect.SetBoneTransforms(bones);
                    effect.World = worldMatrix;
                    effect.View = view;
                    effect.Projection = projection;
                    
                    effect.EnableDefaultLighting();

                    effect.SpecularColor = new Vector3(0.25f);
                    effect.SpecularPower = 16;


                }


                // Draw the mesh, using the effects set above.
                mesh.Draw();
            }
        }

        private void DrawRefractionMap()
        {
            Plane refractionPlane = CreatePlane(waterHeight + 1.5f, new Vector3(0, -1, 0), viewMatrix, false);

            effect.Parameters["ClipPlane0"].SetValue(new Vector4(refractionPlane.Normal, refractionPlane.D));
            effect.Parameters["Clipping"].SetValue(true);   //Allows the geometry to be clipped for the purpose of creating a refraction map
            
            device.SetRenderTarget(refractionRenderTarget);
            device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
            //DrawTerrain(viewMatrix);
            DrawLandScapeModel(viewMatrix);
            device.SetRenderTarget(null);

            effect.Parameters["Clipping"].SetValue(false);  // Make sure you turn it back off so the whole scene doesn't keep rendering as clipped
            refractionMap = refractionRenderTarget;

            
        }
        private void DrawReflectionMap()
        {
            Plane reflectionPlane = CreatePlane(waterHeight - 0.5f, new Vector3(0, -1, 0), reflectionViewMatrix, true);
            
            effect.Parameters["ClipPlane0"].SetValue(new Vector4(reflectionPlane.Normal, reflectionPlane.D));
            effect.Parameters["Clipping"].SetValue(true);   //Allows the geometry to be clipped for the purpose of creating a reflection map

            device.SetRenderTarget(reflectionRenderTarget);
            device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
            DrawSkyDome();
           // DrawTerrain(reflectionViewMatrix);
            DrawLandScapeModel(reflectionViewMatrix);
            

            effect.Parameters["Clipping"].SetValue(false);  // Make sure you turn it back off so the whole scene doesn't keep rendering as clipped
            device.SetRenderTarget(null);

            reflectionMap = reflectionRenderTarget;
            
        }

        private void DrawWater(float time)
        {
            Matrix wMatrix = Matrix.CreateTranslation(0, -0.4f, 0) * Matrix.CreateScale(40) * Matrix.CreateTranslation(waterPosition);

            effect.CurrentTechnique = effect.Techniques["Water"];
            
            effect.Parameters["xWorld"].SetValue(wMatrix);
            effect.Parameters["xView"].SetValue(viewMatrix);
            effect.Parameters["xReflectionView"].SetValue(reflectionViewMatrix);
            effect.Parameters["xProjection"].SetValue(projectionMatrix);
            effect.Parameters["xReflectionMap"].SetValue(reflectionMap);
            effect.Parameters["xRefractionMap"].SetValue(refractionMap);
            effect.Parameters["xWaterBumpMap"].SetValue(waterBumpMap);
            effect.Parameters["xWaveLength"].SetValue(0.1f);
            effect.Parameters["xWaveHeight"].SetValue(0.3f);
            effect.Parameters["xCamPos"].SetValue(cameraPos);
            effect.Parameters["xTime"].SetValue(time);
            effect.Parameters["xWindDirection"].SetValue(windDirection);
            effect.Parameters["xWindForce"].SetValue(0.0005f);


            effect.CurrentTechnique.Passes[0].Apply();
            device.SetVertexBuffer(waterVertexBuffer);

               
                device.DrawPrimitives(PrimitiveType.TriangleList, 0, waterVertexBuffer.VertexCount/ 3);
                


            



        }
#endregion

        #region Handle Input


        /// <summary>
        /// Handles input for quitting the game.
        /// </summary>
        private void HandleInput()
        {
            currentKeyboardState = Keyboard.GetState();
            currentGamePadState = GamePad.GetState(PlayerIndex.One);

            // Check for exit.
            if (currentKeyboardState.IsKeyDown(Keys.Escape) ||
                currentGamePadState.Buttons.Back == ButtonState.Pressed)
            {
                Exit();
            }
        }

        private void CheckWalkOrStand(GameTime gameTime) {

            float time = (float)gameTime.ElapsedGameTime.TotalMilliseconds;

            if ((currentKeyboardState.IsKeyDown(Keys.Up)
                && previousKeyboardState.IsKeyUp(Keys.Up)) || (currentKeyboardState.IsKeyDown(Keys.W) && previousKeyboardState.IsKeyUp(Keys.W)) || (currentKeyboardState.IsKeyDown(Keys.Down)
                && previousKeyboardState.IsKeyUp(Keys.Down)) || (currentKeyboardState.IsKeyDown(Keys.S) && previousKeyboardState.IsKeyUp(Keys.S)))
            {
                currentModel = walkingModel;


                playerWalk.StartClip(clipWalk);
                
                previousKeyboardState = currentKeyboardState;
            }
            if ((currentKeyboardState.IsKeyUp(Keys.Up) && previousKeyboardState.IsKeyDown(Keys.Up)) || (currentKeyboardState.IsKeyUp(Keys.W) && previousKeyboardState.IsKeyDown(Keys.W)) || (currentKeyboardState.IsKeyUp(Keys.Down) && previousKeyboardState.IsKeyDown(Keys.Down)) || (currentKeyboardState.IsKeyUp(Keys.S) && previousKeyboardState.IsKeyDown(Keys.S)))
            {
                currentModel = standingModel;

                playerStand.StartClip(clipStand);
                
                previousKeyboardState = currentKeyboardState;
            }

        }

        private void CheckMoveOrRotateModel()
        {
            //MouseState currentMouseState = Mouse.GetState();
            //if (currentMouseState != originalMouseState)
            //{
            //    float xDifference = currentMouseState.X - originalMouseState.X;
            //    float yDifference = currentMouseState.Y - originalMouseState.Y;
            //    leftrightRot -= rotationSpeed * xDifference * amount;
            //    updownRot -= rotationSpeed * yDifference * amount;
            //    Mouse.SetPosition(device.Viewport.Width / 2, device.Viewport.Height / 2);
            //    UpdateViewMatrix();
            //}

           // Vector3 moveVector = new Vector3(0, 0, 0);
            KeyboardState keyState = Keyboard.GetState();
            if (keyState.IsKeyDown(Keys.W) || keyState.IsKeyDown(Keys.Up))
                
            Move(new Vector3(0, 0, -1f));
                if (keyState.IsKeyDown(Keys.S) || keyState.IsKeyDown(Keys.Down))
                    Move(new Vector3(0, 0, 1f));
            if (keyState.IsKeyDown(Keys.Right) || keyState.IsKeyDown(Keys.D))
              
                leftRightRot -= .05f;
            if (keyState.IsKeyDown(Keys.Left) || keyState.IsKeyDown(Keys.A))
              leftRightRot += .05f;
            //if (keyState.IsKeyDown(Keys.Q))
            
            //if (keyState.IsKeyDown(Keys.Z))
              
       //     AddToModelPosition(moveVector * amount);
            Quaternion additionalRot = Quaternion.CreateFromAxisAngle(new Vector3(0, 1, 0), leftRightRot);
            modelRotation *= additionalRot;
            leftRightRot = 0;
           
            
        }

        private void Move(Vector3 direction)
        {
            Vector3 addVector = Vector3.Transform(direction, modelRotation);
            
                lastMovedVector = addVector;
            
            modelPosition += addVector;
        }
        

        #endregion

    }
}
